{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ">参考：[Gstreamer视频传输测试gst-launch](https://blog.csdn.net/meng_tianshi/article/details/80142005)\n",
    "\n",
    "首先注意版本 我的ubuntu奇怪的安装了两个版本的gstreamer，而且他们的插件不共享\n",
    "\n",
    "- gst-launch-0.10\n",
    "- gst-launch-1.0\n",
    "\n",
    "注意　gstreamer0.10与gstreamer1.0中一些插件的使用，有时不太一样，用gst-inspect仔细看看\n",
    "比如videotestsrc插件\n",
    "\n",
    "- gst-0.1 video/x-raw-yuv\n",
    "- gst-1.0 video/x-raw"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 基础测试\n",
    "\n",
    "## 测试样式\n",
    "\n",
    "- smpte        \n",
    "- snow        \n",
    "- black        \n",
    "- white        \n",
    "- red          \n",
    "- green        \n",
    "- blue         \n",
    "- checkers-1   \n",
    "- checkers-2   \n",
    "- checkers-4   \n",
    "- checkers-8  \n",
    "- circular    \n",
    "- blink       \n",
    "- zone-plate \n",
    "- smpte75       \n",
    "- zone-plate     \n",
    "- gamut         \n",
    "- chroma-zone-plate \n",
    "- solid-color      \n",
    "- ball         \n",
    "- smpte100       \n",
    "- bar           \n",
    "- pinwheel       \n",
    "- spokes         \n",
    "- gradient       \n",
    "- colors         \n",
    "\n",
    "\n",
    "## 不同sink测试\n",
    "\n",
    "`ximagesink`和`xvimagesink` 两者都是用来x桌面用来显示视频图像的sink插件，但支持的格式不同:  \n",
    "`ximagesink`支持rgb格式，不支持yuv格式   \n",
    "`xvimagesink`支持yuv等多种格式\n",
    "\n",
    "- gst-launch-1.0 videotestsrc pattern=ball ! ximagesink\n",
    "- gst-launch-1.0 videotestsrc pattern=ball ! videoconvert ! ximagesink\n",
    "- gst-launch-1.0 videotestsrc pattern=ball ! videoconvert ! xvimagesink\n",
    "- gst-launch-1.0 videotestsrc pattern=ball ! videoconvert ! autovideosink\n",
    "\n",
    "`videoconvert` 用于颜色转换的插件 转换多种颜色格式的过滤器，摄像头设备视频数据通常使用YUV颜色格式，而显示器通常使用RGB颜色格式\n",
    "\n",
    "## 显示摄像头\n",
    "\n",
    "- gst-launch-1.0 v4l2src device=/dev/video0  ! autovideosink\n",
    "- gst-launch-1.0 v4l2src device=/dev/video0 ! videoconvert ! ximagesink\n",
    "- gst-launch-1.0 v4l2src device=/dev/video0  ! appsink\n",
    "\n",
    "## 添加参数\n",
    "\n",
    "尺寸，帧率，格式等\n",
    "\n",
    "方式一：\n",
    "\n",
    "- gst-launch-1.0 v4l2src device=/dev/video0  !  video/x-raw, width=1280, height=720, framerate=20/1  !  autovideosink\n",
    "\n",
    "- gst-launch-1.0 videotestsrc  pattern=ball  ! video/x-raw, width=1280, height=720, framerate=20/1 ! videoconvert ! ximagesink\n",
    "\n",
    "1.0 采集摄像头：\n",
    "- gst-launch-1.0 -e v4l2src device=\"/dev/video0\" ! 'video/x-raw, width=(int)640, height=(int)480, format=(string)YUY2' ! videoconvert ! ximagesink\n",
    "\n",
    "- gst-launch-1.0 -e v4l2src device=\"/dev/video0\" ! 'video/x-raw, width=640, height=480, format=YUY2' ! videoconvert ! ximagesink\n",
    "\n",
    "0.10版本采集摄像头：format参数可以用VLC查看\n",
    "\n",
    "- gst-launch-0.10 -e v4l2src device=\"/dev/video0\" ! 'video/x-raw-yuv, width=(int)640, height=(int)480, format=(fourcc)YUY2' ! videoconvert ! ximagesink\n",
    "\n",
    "方式二：\n",
    "\n",
    "- gst-launch-1.0 videotestsrc pattern=ball !  'video/x-raw,width=320,height=240,framerate=30/1' ! videoconvert ! ximagesink\n",
    "\n",
    ">视频采集 ! 设定尺寸320*240 帧率 30 fps ! 颜色转换 ！ 屏幕显示\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 网络传输测试\n",
    "\n",
    "## 测试UDP网络发送 RTP视频流\n",
    "\n",
    "### 1. 发送到本地127.0.0.1 \n",
    "\n",
    "**发送端, 发送测试雪球的视频**\n",
    "\n",
    "- gst-launch-1.0 videotestsrc pattern=ball ! x264enc ! rtph264pay ! udpsink host=127.0.0.1 port=5600  \n",
    "\n",
    "encode a test video source to H264 – RTP发送  \n",
    "可以用mNetAssist网络调试助手 查看地址设为127.0.0.1 port：5600 可以监听到数据\n",
    "\n",
    "**发送端，采集卡**\n",
    "\n",
    "- gst-launch-0.10 v4l2src ! video/x-raw-yuv,width=352,height=288 ! x264enc ! rtph264pay ! udpsink host=127.0.0.1 port=5600\n",
    "\n",
    ">获取视频 ! H.264编码 ! 传输\n",
    "\n",
    "**接收端 另开一个端口 接收视频**\n",
    "- gst-launch-1.0 udpsrc port=5600 caps='application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)H264' ! rtph264depay ! avdec_h264 ! autovideosink fps-update-interval=1000 sync=false\n",
    "\n",
    ">！ 监听端口5600 ！ 解码 ！ 播放视频\n",
    "\n",
    "### 2. 发送到其他主机\n",
    "\n",
    "发送端 TK1 挂载C920 摄像头 连接路由 IP 192.168.0.10*  \n",
    "接收端 PC 连接校园网 IP 172.20.59.13*\n",
    "\n",
    "**发送端**\n",
    "\n",
    "- gst-launch-0.10 v4l2src  device=/dev/video0  !  video/x-raw-yuv,width=352,height=288 !  x264enc ! rtph264pay ! udpsink host=172.20.59.13* port=5000\n",
    " \n",
    "注意几个关键的点 video/x-raw-yuv  x264enc rtph264pay\n",
    "\n",
    "**接收端**\n",
    "\n",
    "- gst-launch-1.0 udpsrc port=5000 caps='application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)H264' ! rtph264depay ! avdec_h264 ! autovideosink fps-update-interval=1000 sync=false\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 推送RTSP（TCP）\n",
    "\n",
    "### 推送测试"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import gi\n",
    "\n",
    "gi.require_version('Gst', '1.0')\n",
    "gi.require_version('GstVideo', '1.0')\n",
    "gi.require_version('GstRtspServer', '1.0')\n",
    "\n",
    "from gi.repository import GObject, Gst, GstVideo, GstRtspServer\n",
    "\n",
    "Gst.init(None)\n",
    "\n",
    "mainloop = GObject.MainLoop()\n",
    "\n",
    "server = GstRtspServer.RTSPServer()\n",
    "# 绑定地址、端口号\n",
    "server.set_address(\"0.0.0.0\")\n",
    "server.set_service(\"8554\")\n",
    "\n",
    "mounts = server.get_mount_points()\n",
    "\n",
    "factory = GstRtspServer.RTSPMediaFactory()\n",
    "factory.set_launch('(videotestsrc pattern=ball is-live=1 ! video/x-raw, width=820, height=540, framerate=30/1, format=I420 ! x264enc speed-preset=ultrafast tune=zerolatency ! h264parse ! rtph264pay name=pay0 pt=96 )')\n",
    "# 或者\n",
    "# factory.set_launch('( videotestsrc is-live=1 ! x264enc speed-preset=ultrafast tune=zerolatency ! rtph264pay name=pay0 pt=96 )')\n",
    "# factory.set_launch('( videotestsrc is-live=1 !avenc_h254_omx ! rtph264pay name=pay0 pt=96 )')\n",
    "\n",
    "\n",
    "mounts.add_factory(\"/test\", factory)\n",
    "\n",
    "server.attach(None)\n",
    "\n",
    "print(\"stream ready at rtsp://127.0.0.1:8554/test\")\n",
    "mainloop.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 采集摄像头并推送"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import gi\n",
    "\n",
    "gi.require_version('Gst', '1.0')\n",
    "gi.require_version('GstVideo', '1.0')\n",
    "gi.require_version('GstRtspServer', '1.0')\n",
    "\n",
    "from gi.repository import GObject, Gst, GstVideo, GstRtspServer\n",
    "\n",
    "Gst.init(None)\n",
    "\n",
    "mainloop = GObject.MainLoop()\n",
    "\n",
    "server = GstRtspServer.RTSPServer()\n",
    "\n",
    "mounts = server.get_mount_points()\n",
    "\n",
    "factory = GstRtspServer.RTSPMediaFactory()\n",
    "factory.set_launch('( v4l2src ! video/x-raw,width=640,height=480 ! x264enc speed-preset=ultrafast tune=zerolatency ! rtph264pay name=pay0 pt=96 )')\n",
    "\n",
    "mounts.add_factory(\"/test\", factory)\n",
    "\n",
    "server.attach(None)\n",
    "\n",
    "print(\"stream ready at rtsp://127.0.0.1:8554/test\")\n",
    "mainloop.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 推送RTMP\n",
    "\n",
    "### 准备rtmp 推流服务器\n",
    "\n",
    "我们用 nginx 加上 nginx-rtmp-module 模块作为 RTMP 服务端。这里先安装 nginx 然后再移除它，目的是利用这个过程吧 nginx 相关的依赖安装好并设定好系统环境。这个通过 apt 安装的 nginx 并不能直接使用，因为他并不包含我们需要的 RTMP 模块，所以将它移除。而后我们手工下载 nginx 和 nginx-rtmp-module 模块源码来手工编译安装，以获得我们最终需要的服务端。\n",
    "\n",
    "```sh\n",
    "sudo apt-get update\n",
    "#安装 nginx\n",
    "sudo apt-get -y install nginx\n",
    "#移除 nginx\n",
    "sudo apt-get -y remove nginx\n",
    "sudo apt-get clean\n",
    "#清空 nginx 的配置文件\n",
    "sudo rm -rf /etc/nginx/*\n",
    "#安装编译用的模块\n",
    "sudo apt-get install -y curl build-essential libpcre3 libpcre3-dev libpcre++-dev zlib1g-dev libcurl4-openssl-dev libssl-dev\n",
    "#创建存放网页的目录给 nginx 使用\n",
    "sudo mkdir -p /var/www\n",
    "#创建编译用的目录\n",
    "mkdir -p ~/nginx_src\n",
    "cd ~/nginx_src\n",
    "#下载 nginx 源码包\n",
    "wget http://nginx.org/download/nginx-1.11.8.tar.gz\n",
    "#下载 nginx-rtmp-module 源码包\n",
    "wget https://github.com/arut/nginx-rtmp-module/archive/master.zip\n",
    "tar -zxvf nginx-1.11.8.tar.gz\n",
    "unzip master.zip\n",
    "cd nginx-1.11.8\n",
    "#设定编译参数\n",
    "./configure --prefix=/var/www --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --pid-path=/var/run/nginx.pid --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --with-http_ssl_module --without-http_proxy_module --add-module=/home/pi/nginx_src/nginx-rtmp-module-master\n",
    "#开始编译安装\n",
    "make\n",
    "sudo make install\n",
    "```\n",
    "\n",
    "可能遇到的问题: [In function ‘ngx_murmur_hash2’等错误](https://blog.csdn.net/zolewit/article/details/93595253)\n",
    "\n",
    "比较漫长的等待之后，编译安装结束。这时可以测试 nginx 是否安装好。\n",
    "\n",
    "```sh\n",
    "nginx -v\n",
    "```\n",
    "\n",
    "正常的話，会提示 nginx 的版本。\n",
    "\n",
    "配置 nginx。\n",
    "\n",
    "```\n",
    "/etc/nginx/nginx.conf\n",
    "```\n",
    "**在末尾直接添加如下配置**：\n",
    "\n",
    "```\n",
    "rtmp {\n",
    "    server {\n",
    "        listen 1935;\n",
    "        chunk_size 4096;\n",
    "        application live { \n",
    "            live on;\n",
    "            record off;\n",
    "        }\n",
    "    }\n",
    "}\n",
    "```\n",
    "**注意： 此处application 路径为 live** \n",
    "\n",
    "重启 nginx 服务。\n",
    "\n",
    "```sh\n",
    "sudo service nginx start\n",
    "```\n",
    "\n",
    "（若还是提示socket 写入错误，就**重启机器**）\n",
    "\n",
    "### 推送测试\n",
    "\n",
    "- gst-launch-1.0 videotestsrc pattern=snow ! video/x-raw, width=640, height=480, framerate=30/1 ! queue ! videoconvert ! x264enc speed-preset=ultrafast tune=zerolatency !  h264parse ! flvmux ! rtmpsink location='rtmp://10.86.23.199/live'\n",
    "\n",
    "### 采集摄像头\n",
    "- gst-launch-1.0 -v v4l2src device=/dev/video0 ! 'video/x-raw, width=(int)640, height=(int)480, format=(string)YUY2' ! queue ! videoconvert ! x264enc ! h264parse ! flvmux ! rtmpsink location='rtmp://10.86.23.199/live'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 播放RTSP\n",
    "\n",
    "## GUI显示\n",
    "\n",
    "用 videoconvert 转换才能播放出来\n",
    "\n",
    "- gst-launch-1.0 -v rtspsrc user-id=admin user-pw=admin location=rtsp://10.86.77.12:554/h264/ch1/sub/av_stream ! decodebin ! videoconvert ! autovideosink \n",
    "\n",
    "- gst-launch-1.0 rtspsrc name=src location=rtsp://admin:admin@10.86.77.12:554/h264/ch1/sub/av_stream latency=10 ! decodebin ! videoconvert ! video/x-raw,format=BGRx ! ximagesink\n",
    "\n",
    "- gst-launch-1.0 rtspsrc name=src location=rtsp://admin:admin@10.86.77.12:554/h264/ch1/sub/av_stream latency=10 ! decodebin ! videoconvert ! ximagesink\n",
    "\n",
    "- gst-launch-1.0 rtspsrc name=src location=rtsp://admin:admin@10.86.77.12:554/h264/ch1/sub/av_stream latency=10 ! decodebin ! video/x-raw,format=I420 ! videoconvert ! video/x-raw,format=BGRx ! ximagesink\n",
    "\n",
    ">可指定 rtp 包传输协议为 TCP，默认为\"tcp+udp-mcast+udp\"\n",
    "\n",
    "**decodebin 是一系列组件的结合**，等价于：（rtph264depay + ）h264parse + avdec_h264\n",
    "\n",
    "- gst-launch-1.0 rtspsrc name=src protocols=tcp location=rtsp://admin:admin@10.86.77.12:554/h264/ch1/sub/av_stream latency=10 ! rtph264depay ! h264parse ! avdec_h264 ! videoconvert ! ximagesink \n",
    "\n",
    "- gst-launch-1.0 rtspsrc name=src protocols=tcp location=rtsp://admin:admin@10.86.77.12:554/h264/ch1/sub/av_stream latency=10 ! rtph264depay ! video/x-h264, width=704, height=576 ! avdec_h264 ! videoconvert !  ximagesink\n",
    "\n",
    "- gst-launch-1.0 rtspsrc name=src location=rtsp://admin:admin@10.86.77.12:554/h264/ch1/sub/av_stream latency=10 ! rtph264depay ! h264parse ! avdec_h264 ! video/x-raw,format=I420 ! videoconvert ! video/x-raw,format=BGRx ! ximagesink"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Appsink\n",
    "\n",
    "### 传入I420 (h264解码后YUV可能格式不一）\n",
    "\n",
    "- gst-launch-1.0 rtspsrc name=src location=rtsp://admin:admin@10.86.77.12:554/h264/ch1/sub/av_stream latency=10 ! decodebin ! appsink name=sink\n",
    "\n",
    "### 传入RGB\n",
    "\n",
    "I420-RGB格式转换 \n",
    "\n",
    "- gst-launch-1.0 rtspsrc name=src location=rtsp://admin:admin@10.86.77.12:554/h264/ch1/sub/av_stream latency=10 ! rtph264depay ! h264parse ! avdec_h264 ! video/x-raw,format=I420 ! videoconvert ! video/x-raw,format=RGB ! appsink name=sink\n",
    "\n",
    "任意格式转RGB\n",
    "\n",
    "- gst-launch-1.0 rtspsrc name=src location=rtsp://admin:admin@10.86.77.12:554/h264/ch1/sub/av_stream latency=10 ! decodebin ! videoconvert ! video/x-raw,format=RGB ! appsink name=sink\n",
    "\n",
    "### Jetson\n",
    "\n",
    "- [在Jetson TX2上捕获、显示摄像头视频](http://www.bubuko.com/infodetail-3227583.html)\n",
    "\n",
    "用 nvvidconv 进行图像缩放并将颜色格式转换为BGRx可以在帧率产生更好的效果\n",
    "\n",
    "- gst-launch-1.0 rtspsrc location=rtsp://admin:admin666@192.168.1.64:554/h264/ch1/main/av_stream latency=0 ! rtph264depay ! h264parse ! omxh264dec ! nvvidconv ! videoconvert ! xvimagesink sync=false\n",
    "\n",
    "- rtspsrc location=rtsp://admin:admin666@192.168.1.64:554/h264/ch1/main/av_stream  latency=1 ! rtph264depay ! h264parse ! omxh264dec ! nvvidconv ! video/x-raw, width=704, height=576, format=BGRx ! videoconvert ! appsink"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 代码示例\n",
    "\n",
    "实现：  \n",
    "gst-launch-1.0 rtspsrc name=src protocols=tcp location=rtsp://admin:admin@10.86.77.12:554/h264/ch1/sub/av_stream latency=10 ! rtph264depay ! queue2 ! capsfilter ! video/x-h264, width=704, height=576 ! avdec_h264 ! videoconvert ! ximagesink\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import gi\n",
    "gi.require_version('Gst', '1.0')\n",
    "from gi.repository import Gst, GObject, GLib\n",
    "\n",
    "Gst.init(None)\n",
    "a = 0\n",
    "\n",
    "#rtspsrc的srcpad是随机衬垫，这里使用回调函数来连接衬垫。\n",
    "def on_pad_added(src, pad, des):\n",
    "    vpad = des.get_static_pad(\"sink\")\n",
    "    pad.link(vpad)\n",
    "\n",
    "pipe = Gst.Pipeline.new(\"test\")\n",
    "src = Gst.ElementFactory.make(\"rtspsrc\", \"src\")\n",
    "depay = Gst.ElementFactory.make(\"rtph264depay\", \"depay\")\n",
    "queuev1 = Gst.ElementFactory.make(\"queue2\", \"queue\")\n",
    "src.connect(\"pad-added\", on_pad_added, queuev1)\n",
    "#过滤\n",
    "vfilter = Gst.ElementFactory.make(\"capsfilter\", \"flt\")\n",
    "caps = Gst.Caps.from_string(\"video/x-h264, width=704, height=576\")\n",
    "vfilter.set_property(\"caps\", caps)\n",
    "\n",
    "conv = Gst.ElementFactory.make(\"videoconvert\", \"conv\")\n",
    "sink = Gst.ElementFactory.make(\"ximagesink\", \"sink\")\n",
    "\n",
    "decoder = Gst.ElementFactory.make(\"avdec_h264\", \"decodea\")\n",
    "\n",
    "rstp = 'rtsp://admin:admin@10.86.77.12:554/h264/ch1/sub/av_stream'\n",
    "src.set_property(\"location\", rstp)\n",
    "\n",
    "pipe.add(src)\n",
    "pipe.add(depay)\n",
    "pipe.add(queuev1)\n",
    "pipe.add(vfilter)\n",
    "pipe.add(decoder)\n",
    "pipe.add(conv)\n",
    "pipe.add(sink)\n",
    "\n",
    "queuev1.link(depay)\n",
    "depay.link(vfilter)\n",
    "vfilter.link(decoder)\n",
    "decoder.link(conv)\n",
    "conv.link(sink)\n",
    "\n",
    "pipe.set_state(Gst.State.PLAYING)\n",
    "\n",
    "mainloop = GLib.MainLoop()\n",
    "mainloop.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 播放RTMP\n",
    "\n",
    "- gst-launch-1.0 --gst-debug-level=2 rtmpsrc location='rtmp://58.200.131.2:1935/livetv/hunantv' ! flvdemux ! queue ! decodebin ! videoconvert ! ximagesink\n",
    "\n",
    "等价于\n",
    "- gst-launch-1.0 --gst-debug-level=2 rtmpsrc location='rtmp://58.200.131.2:1935/livetv/hunantv' ! flvdemux ! queue ! h264parse ! avdec_h264 ! videoconvert ! ximagesink"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "232.8px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
